#include <errno.h>
#include <exception>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>

using Closer = int (*)(FILE *);
using FilePtr = std::unique_ptr<FILE, Closer>;

constexpr void assert_that(bool statement, const char *msg) {
  if (!statement) {
    throw std::runtime_error(msg);
  }
}

// use std::make_shared and std::make_unique instead
// using uptr = std::unique_ptr<T>
// bool raise() throw exception
// foo(uptr(new T), raise(), uptr(new T)), the param evaluate order unspecity
int main(int argc, char *argv[]) {
  assert_that(argc == 2, "Usage: command [path]");

  FILE *f = nullptr;

  f = fopen(argv[1], "r+");

  assert_that(f, strerror(errno));

  // assign FILE* to a unique_ptr
  FilePtr fptr{f, fclose};

  assert_that(!!fptr, strerror(errno));
  assert_that(fseek(fptr.get(), 0, SEEK_END) == 0, strerror(errno));

  long size = ftell(fptr.get());

  assert_that(size > 0, strerror(errno));

  rewind(fptr.get());

  // using unique_ptr to create a buffer instead of using malloc
  std::unique_ptr<char[]> buf{new char[size + 1]{0}};
  assert_that(!!buf, strerror(errno));

  size_t r = fread(buf.get(), 1, size, fptr.get());

  assert_that(r == size, "Reading error");

std:
  std::cout << buf.get();

end:

  return 0;
}
